#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <limits.h>

int inicializacion(int *argc, char *argv[], int blockHeight, int blockWidth, int *frameWidth, int *frameHeight, 
                   unsigned char **refFrame, unsigned char **curFrame, int **matchBlock);
int blockMatch4x4(const unsigned char* refFrame, const unsigned char* curFrame, 
				  int* results, int frameWidth, int frameHeight, int blockWidth, int blockHeight);

int main( int argc, char *argv[] ) 
{
	unsigned char *refFrame;
	unsigned char *curFrame, *pcur;
	int *results;
	int frameWidth;
	int frameHeight;
	int i=0, j=0;
	struct timeval t0, t1, t;
	// Nº de pixels del bloque
	int blockWidth = 4;
	int blockHeight = 4;
	
	inicializacion(&argc, argv, blockHeight, blockWidth, &frameWidth, &frameHeight, &refFrame, &curFrame, &results);
	
	assert (gettimeofday (&t0, NULL) == 0);
	
	pcur=&(curFrame[0]);
	printf ("info: Executing the algorithm (procedure blockMatch4x4) ...\n");
	blockMatch4x4(refFrame,pcur,results,frameWidth,frameHeight,blockWidth,blockHeight);
	printf ("info: Block (%d,%d) of curFrame matches block (%d,%d) of refFrame (lowsum=%d)\n", 
		i, j, results[0], results[1], results[2]);
	
	assert (gettimeofday (&t1, NULL) == 0);
	timersub(&t1, &t0, &t);
	printf ("info: Memoria       = %ld KB\n", (2*frameWidth*frameHeight*sizeof(unsigned char))/1024 );
	printf ("info: Tiempo total  = %ld seg : %ld mseg\n", t.tv_sec, t.tv_usec/1000);
	
	return 0;
}

int blockMatch4x4(const unsigned char* refFrame, const unsigned char* curFrame, 
				  int* results, int frameWidth, int frameHeight, int blockWidth, int blockHeight)
{
	int lowSum = INT_MAX;
	int temSum = 0;
	int i,j,k,l;
	


	for (i=0; i<=frameHeight-blockHeight; i++)
	{
		for (j=0; j<=frameWidth-blockWidth; j++)  
		{
			temSum = 0;
			for (k=0; k<blockHeight; k++)
			{
				for (l=0; l<blockWidth; l++)
				{
					temSum += labs(refFrame[(i+k)*frameWidth+(j+l)]-curFrame[k*frameWidth+l]);
				}
			}
			
			if (temSum < lowSum)
			{
				lowSum = temSum;
				results[0] = i;
				results[1] = j;
				results[2] = lowSum;
			}
		}
	}


	return 0;
}


int inicializacion(int *argc, char *argv[], int blockHeight, int blockWidth, int *frameWidth, int *frameHeight, 
                   unsigned char **refFrame, unsigned char **curFrame, int **matchBlock)
{
	int i,j;
    unsigned char tmp;
	
    /* Check arguments */
    switch ( (*argc) ) {
		case 3:
			break;
		default:
			fprintf(stderr,"Usage: %s <frameWidth> <frameHeight> \n",argv[0]);
			exit(-1);
			break;
    }
	(*frameWidth) = atoi(argv[1]);
	(*frameHeight) = atoi(argv[2]);
	printf ("info: frameWidth = %d\n", (*frameWidth));
	printf ("info: frameHeight = %d\n", (*frameHeight));
	if( ((*refFrame)=(unsigned char *) malloc(sizeof(unsigned char)*(*frameWidth)*(*frameHeight))) == NULL ) {
		printf ("error: memoria insuficiente para refFrame\n");
		exit(-1);
	}
	printf ("info: refFrame[%dx%d] allocated\n", (*frameWidth), (*frameHeight));
	if( ((*curFrame)=(unsigned char *) malloc(sizeof(unsigned char)*(*frameWidth)*(*frameHeight))) == NULL ) {
		printf ("error: memoria insuficiente para curFrame\n");
		exit(-1);
	}
	printf ("info: curFrame[%dx%d] allocated\n", (*frameWidth), (*frameHeight));
	srand(1);
	for(i=0;i<((*frameWidth)*(*frameHeight));i++) {
		(*refFrame)[i]=(*curFrame)[i]=(unsigned char) (rand()%256);
	}
	printf ("info: refFrame[%dx%d] initialized\n", (*frameWidth), (*frameHeight));
	printf ("info: curFrame[%dx%d] initialized as a copy of refFrame[%dx%d]\n", (*frameWidth), (*frameHeight), (*frameWidth), (*frameHeight));
	printf ("info: swap block (0,0) and block (4,0) of curFrame\n");
	for( i=0 ; i<blockHeight ; i++ ) {
		for( j=0 ; j<blockWidth ; j++ ) {
		    printf("info: swap curFrame[%ld] and curFrame[%ld]\n", (i*(*frameWidth)+j), ((blockHeight+i)*(*frameWidth)+j));
            tmp = (*curFrame)[i*(*frameWidth)+j];
			(*curFrame)[i*(*frameWidth)+j] = (*curFrame)[(blockHeight+i)*(*frameWidth)+j];
			(*curFrame)[(blockHeight+i)*(*frameWidth)+j] = tmp;
		}
	}
	printf ("info: curFrame[%dx%d] initialized\n", (*frameWidth), (*frameHeight));
	if( ((*matchBlock)=(int *) malloc(sizeof(int)*3)) == NULL ) {
		printf ("error: memoria insuficiente para matchBlock\n");
		exit(-1);
	}
	printf ("info: matchBlock[3] allocated\n");
	
}
